<template>
	<div class="sc-upload" :class="{ 'sc-upload-round': round }" :style="style">
		<div v-if="file && file.status != 'success'" class="sc-upload__uploading">
			<div class="sc-upload__progress">
				<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16" />
			</div>
			<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
		</div>
		<div v-if="file && file.status == 'success'" class="sc-upload__img">
			<el-image class="image" :src="file.url" :preview-src-list="[file.url]" fit="cover" hide-on-click-modal append-to-body :z-index="9999">
				<template #placeholder>
					<div class="sc-upload__img-slot">Loading...</div>
				</template>
			</el-image>
			<div class="sc-upload__img-actions" v-if="!disabled">
				<span class="del" @click="handleRemove()">
					<el-icon><el-icon-delete /></el-icon>
				</span>
			</div>
		</div>
		<el-upload
			v-if="!file"
			class="uploader"
			ref="uploader"
			:auto-upload="props.cropper ? false : props.autoUpload"
			:disabled="props.disabled"
			:show-file-list="props.showFileList"
			:action="props.action"
			:name="props.name"
			:data="props.data"
			:accept="props.accept"
			:limit="1"
			:http-request="request"
			:on-change="change"
			:before-upload="before"
			:on-success="success"
			:on-error="error"
			:on-exceed="handleExceed">
			<slot>
				<div class="el-upload--picture-card">
					<div class="file-empty">
						<el-icon>
							<component :is="props.icon" />
						</el-icon>
						<h4 v-if="props.title">{{ props.title }}</h4>
					</div>
				</div>
			</slot>
		</el-upload>
		<span style="display: none !important"><el-input v-model="value"></el-input></span>
		<el-dialog title="剪裁" draggable v-model="cropperDialogVisible" :width="580" @closed="cropperClosed" destroy-on-close>
			<sc-cropper :src="cropperFile.tempCropperFile" :compress="compress" :aspectRatio="aspectRatio" ref="cropper"></sc-cropper>
			<template #footer>
				<el-button @click="cropperDialogVisible = false">取 消</el-button>
				<el-button type="primary" @click="cropperSave">确 定</el-button>
			</template>
		</el-dialog>
	</div>
</template>
<script setup lang="ts">
	import { genFileId } from 'element-plus'
	const scCropper = defineAsyncComponent(() => import('@/components/scCropper/index.vue'))
	const emit = defineEmits(['update:modelValue'])
	const props = defineProps({
		modelValue: { type: String, default: '' },
		height: { type: Number, default: 148 },
		width: { type: Number, default: 148 },
		title: { type: String, default: '' },
		icon: { type: String, default: 'el-icon-plus' },
		action: { type: String, default: '' },
		apiObj: { type: Object, default: () => {} },
		name: { type: String, default: files.filename },
		data: { type: Object, default: () => {} },
		accept: { type: String, default: 'image/gif, image/jpeg, image/png' },
		maxSize: { type: Number, default: files.maxSizeFile },
		limit: { type: Number, default: 1 },
		autoUpload: { type: Boolean, default: true },
		showFileList: { type: Boolean, default: false },
		disabled: { type: Boolean, default: false },
		round: { type: Boolean, default: false },
		onSuccess: { type: Function, default: () => true },
		cropper: { type: Boolean, default: false },
		compress: { type: Number, default: 1 },
		aspectRatio: { type: Number, default: NaN }
	})
	const value = ref<string>('')
	const file = ref<any>(null)
	const style = reactive({
		width: props.width + 'px',
		height: props.height + 'px'
	})
	const cropper = ref<any>(null)
	const cropperDialogVisible = ref<boolean>(false)
	const cropperFile = ref<any>(null)
	const uploader = ref<any>(null)
	watch(
		() => props.modelValue,
		(val) => ((value.value = val), newFile(val))
	)
	watch(
		() => value.value,
		(val) => emit('update:modelValue', val)
	)
	onMounted(() => ((value.value = props.modelValue), newFile(props.modelValue)))
	const newFile = (url: string) => (file.value = url ? { status: 'success', url: url } : null)
	const cropperSave = () => {
		cropper.value.getCropFile(
			(img: any) => {
				img.uid = cropperFile.value.uid
				cropperFile.value.raw = img
				file.value = cropperFile.value
				file.value.tempFile = URL.createObjectURL(file.value.raw)
				uploader.value.submit()
			},
			cropperFile.value.name,
			cropperFile.value.type,
			(cropperDialogVisible.value = false)
		)
	}
	const cropperClosed = () => (URL.revokeObjectURL(cropperFile.value.tempCropperFile), delete cropperFile.value.tempCropperFile)
	const handleRemove = () => clearFiles()
	const clearFiles = () => {
		URL.revokeObjectURL(file.value.tempFile)
		value.value = ''
		file.value = ''
		nextTick(() => {
			uploader.value.clearFiles()
		})
	}
	const change = (file: any, files: any) => {
		if (files.length > 1) {
			files.splice(0, 1)
		}
		if (cropper.value && file.status == 'ready') {
			const acceptIncludes = ['image/gif', 'image/jpeg', 'image/png'].includes(file.raw.type)
			if (!acceptIncludes) {
				ElNotification.warning({
					title: '上传文件警告',
					message: '选择的文件非图像类文件'
				})
				return false
			}
			cropperFile.value = file
			cropperFile.value.tempCropperFile = URL.createObjectURL(file.raw)
			cropperDialogVisible.value = true
			return false
		}
		file.value = file
		if (file.status == 'ready') {
			file.tempFile = URL.createObjectURL(file.raw)
		}
	}
	const before = (file: any) => {
		const acceptIncludes = props.accept.replace(/\s/g, '').split(',').includes(file.type)
		if (!acceptIncludes) {
			ElNotification.warning({
				title: '上传文件警告',
				message: '选择的文件非图像类文件'
			})
			clearFiles()
			return false
		}
		const maxSize = file.size / 1024 / 1024 < props.maxSize
		if (!maxSize) {
			ElMessage.warning(`上传文件大小不能超过 ${props.maxSize}MB!`)
			clearFiles()
			return false
		}
	}
	const handleExceed = (files: any) => {
		const file = files[0]
		file.uid = genFileId()
		uploader.value.handleStart(file)
	}
	const success = (res: any, file: any) => {
		URL.revokeObjectURL(file.tempFile)
		delete file.tempFile
		var os = props.onSuccess(res, file)
		if (os != undefined && os == false) {
			nextTick(() => ((file.value = null), (value.value = '')))
		}
		var response = files.parseData(res)
		file.url = response.src
		value.value = file.url
	}
	const error = (err: string) => {
		nextTick(() => clearFiles())
		ElNotification.error({
			title: '上传文件未成功',
			message: err
		})
	}
	const request = (param: any) => {
		var apiObj = props.apiObj ? props.apiObj : files.apiObj
		const data = new FormData()
		data.append(param.filename, param.file)
		for (const key in param.data) {
			data.append(key, param.data[key])
		}
		apiObj
			.post(data, {
				onUploadProgress: (e: { loaded: number; total: number }) => {
					const complete = parseInt((((e.loaded / e.total) * 100) | 0).toString(), 10)
					param.onProgress({ percent: complete })
				}
			})
			.then((res: any) => {
				var response = files.parseData(res)
				if (response.code == files.successCode) {
					param.onSuccess(res)
				} else {
					param.onError(response.msg || '未知错误')
				}
			})
			.catch((err: any) => param.onError(err))
	}
</script>
<style lang="less" scoped>
	.el-form-item.is-error .sc-upload .el-upload--picture-card {
		border-color: var(--el-color-danger);
	}
	.sc-upload .el-upload--picture-card {
		border-radius: 0;
	}
	.sc-upload .uploader,
	.sc-upload:deep(.el-upload) {
		width: 100%;
		height: 100%;
	}
	.sc-upload__img {
		width: 100%;
		height: 100%;
		position: relative;
	}
	.sc-upload__img .image {
		width: 100%;
		height: 100%;
	}
	.sc-upload__img-actions {
		position: absolute;
		top: 0;
		right: 0;
		display: none;
	}
	.sc-upload__img-actions span {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 25px;
		height: 25px;
		cursor: pointer;
		color: #fff;
	}
	.sc-upload__img-actions span i {
		font-size: 12px;
	}
	.sc-upload__img-actions .del {
		background: #f56c6c;
	}
	.sc-upload__img:hover .sc-upload__img-actions {
		display: block;
	}
	.sc-upload__img-slot {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		height: 100%;
		font-size: 12px;
		background-color: var(--el-fill-color-lighter);
	}
	.sc-upload__uploading {
		width: 100%;
		height: 100%;
		position: relative;
	}
	.sc-upload__progress {
		position: absolute;
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		background-color: var(--el-overlay-color-lighter);
		z-index: 1;
		padding: 10px;
	}
	.sc-upload__progress .el-progress {
		width: 100%;
	}
	.sc-upload__uploading .image {
		width: 100%;
		height: 100%;
	}
	.sc-upload .file-empty {
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		flex-direction: column;
	}
	.sc-upload .file-empty i {
		font-size: 28px;
	}
	.sc-upload .file-empty h4 {
		font-size: 12px;
		font-weight: normal;
		color: #8c939d;
		margin-top: 8px;
	}
	.sc-upload.sc-upload-round {
		border-radius: 50%;
		overflow: hidden;
	}
	.sc-upload.sc-upload-round .el-upload--picture-card {
		border-radius: 50%;
	}
	.sc-upload.sc-upload-round .sc-upload__img-actions {
		top: auto;
		left: 0;
		right: 0;
		bottom: 0;
	}
	.sc-upload.sc-upload-round .sc-upload__img-actions span {
		width: 100%;
	}
</style>
